package com.atguigu.filter;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.result.RetVal;
import com.atguigu.result.RetValCodeEnum;
import com.atguigu.util.IpUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpCookie;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.nio.charset.StandardCharsets;
import java.util.List;

@Component//网关过滤器
public class AccessFilter implements GlobalFilter {

    //filterWhiteList=trade.html,myOrder.html,list.html
    @Value("${filter.whiteList}")
    private String filterWhiteList;
    @Autowired
    private RedisTemplate redisTemplate;
    // 创建匹配对象
    private AntPathMatcher antPathMatcher = new AntPathMatcher();


    @Override//鉴权
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        //获取请求request
        ServerHttpRequest request = exchange.getRequest();
        //获取请求当中的路径 如这里只会得到search.html路径 http://search.gmall.com/search.html?keyword=三星
        String path = request.getURI().getPath();
        //这个path中不能访问/sku/getSkuInfo/{skuId}中的数据信息
        if(antPathMatcher.match("/sku/**",path)){
            //写数据信息给浏览器
            ServerHttpResponse response = exchange.getResponse();
            return writeDataToBrowser(response, RetValCodeEnum.NO_PERMISSION);
        }



        //获取用户的id信息
        String userId = getLoginUserId(request);
        //cookie被盗 提示没有权限
        if ("-1".equals(userId)){
            ServerHttpResponse response = exchange.getResponse();
            return writeDataToBrowser(response, RetValCodeEnum.NO_PERMISSION);
        }



        //如果要看订单相关信息是需要进行登录的
        if (antPathMatcher.match("/order/**",path)){
            //如果是未登录情况
            if (StringUtils.isEmpty(userId)){
                ServerHttpResponse response = exchange.getResponse();
                return writeDataToBrowser(response, RetValCodeEnum.NO_LOGIN);
            }
        }


        //访问拦截路径名单 对于这些请求路径需要进行拦截
        for (String filterWhite : filterWhiteList.split(",")) {
            //用户访问的url中包含白名单路径，并且用户未登录
            if (path.indexOf(filterWhite)!=-1 && StringUtils.isEmpty(userId)){
                //设置完成之后重定向：跳转到登录页面
                ServerHttpResponse response = exchange.getResponse();
                response.setStatusCode(HttpStatus.SEE_OTHER);
                response.getHeaders().set(HttpHeaders.LOCATION,"http://www.gmall.com/login.html?originalUrl="+request.getURI());
                return response.setComplete();
            }
        }


        //用户id需要保存在header中，传递给shop_web
        //如果说用户登录了呢，那么我们就需要将userId放到request中
        if (!StringUtils.isEmpty(userId)){
            if (!StringUtils.isEmpty(userId)){
                //将用户Id放入header中
                request.mutate().header("userId",userId).build();
            }
            return chain.filter(exchange.mutate().request(request).build());
        }


        //过滤器放开拦截路径继续执行
        return chain.filter(exchange);

    }

    //编写临时id
    private String getUserTempId(ServerHttpRequest request) {
        //从header和cookie中拿到token
        List<String> headerValueList = request.getHeaders().get("userTempId");
        String userTempId=null;
        if(!CollectionUtils.isEmpty(headerValueList)){
            userTempId = headerValueList.get(0);
        }else{
            HttpCookie cookie = request.getCookies().getFirst("userTempId");
            if(cookie!=null){
                userTempId = cookie.getValue();
            }
        }
        return userTempId;
    }

    //获取登录用户Id
    private String getLoginUserId(ServerHttpRequest request) {
        /**
         * 获取登录用户id信息(反着写代码)
         * 1.用户id信息存储在redis中
         * 2.那么就得先拼接redis的key---> String userKey = RedisConst.USER_LOGIN_KEY_PREFIX+token;
         * 3.如何拿到token 从cookie(pc端) header(移动端)
         */
        String token = "";
        //header一般存储在移动端
        List<String> result = request.getHeaders().get("token");
        if (!CollectionUtils.isEmpty(result)){
            token = result.get(0);
        }else {
            //如果header中没有获取到token 那么从cookie里面去获取
            HttpCookie cookie = request.getCookies().getFirst("token");
            if (cookie!=null){
                token = cookie.getValue();
            }
        }
        if (!StringUtils.isEmpty(token)){
            //根据token获取用户登录信息
            String userKey = "user:login:"+token;
            String loginInfoJson = (String) redisTemplate.opsForValue().get(userKey);
            JSONObject loginInfo = JSONObject.parseObject(loginInfoJson);
            String loginIp = loginInfo.getString("loginIp");
            //获取到登录时ip
            String currentIpAddress = IpUtil.getGatwayIpAddress(request);
            //判断当前电脑登录ip是否与存储ip一致 如果一致取得userId,如果不一致返回"-1"
            if (loginIp.equals(currentIpAddress)){
                //防止cookie被盗用所以做了一步判断
                return loginInfo.getString("userId");
            }else {
                return  "-1";
            }
        }
        return null;
    }


    //把数据写到浏览器中
    private Mono<Void> writeDataToBrowser(ServerHttpResponse response, RetValCodeEnum retValCodeEnum) {
        RetVal<Object> retVal = RetVal.build(null, retValCodeEnum);
        //把对象转换为字节
        byte[] bytes = JSONObject.toJSONString(retVal).getBytes(StandardCharsets.UTF_8);
        //将字节数组变为一个数据buffer
        DataBuffer dataBuffer = response.bufferFactory().wrap(bytes);
        //设置页面的头部信息为返回json数据编码为utf-8
        response.getHeaders().add("Content-Type","application/json;charset=UTF-8");
        //把数据写到浏览器
        return response.writeWith(Mono.just(dataBuffer));
    }





}
